package com.googlecode.vanaraha.eclipse
import org.eclipse.jface.viewers.{ISelection,IStructuredSelection}
import scala.collection.mutable
import com.googlecode.vanaraha.utils.Utils.jIterToScalaIter
import org.eclipse.core.resources.{IResource, IContainer, IFile, IFolder, IMarker, ResourcesPlugin}
import com.googlecode.vanaraha.utils.FileUtils.str2readAll
import com.googlecode.vanaraha.data.{SourceFile, DuplicateElement, DuplicateSet}
import org.eclipse.swt.widgets.Shell
import org.eclipse.jface.dialogs.{IInputValidator, InputDialog}
import org.eclipse.ui.PlatformUI
import org.eclipse.core.runtime.{IPath, Path, IProgressMonitor, IStatus, SubMonitor}
import com.googlecode.vanaraha.utils.Utils.logger._
object EclipseUtils {
	type StringPredicate = (String) => Boolean
 import implicits._
 case class InputDialogResult(returnCode: Int, okClicked: Boolean, valueEntered: String)
 
 def activeShell = PlatformUI.getWorkbench.getDisplay.getActiveShell
 
	def inputDialog(shell: Shell, title: String, message: String, initVal: String)
				(validator: (String) => String) = {
	  val inputDialog = new InputDialog(shell, title, message, initVal, new IInputValidator{
		  override def isValid(text: String) = validator(text)
	  })
	  inputDialog.setBlockOnOpen(true)
	  inputDialog.open
	  InputDialogResult(inputDialog.getReturnCode ,
                     inputDialog.getReturnCode == org.eclipse.jface.window.Window.OK,
                     inputDialog.getValue)
	}
    
    def inputDialog(title: String, message: String, initVal: String)
    		(validator: (String) => String) : InputDialogResult =
      inputDialog(activeShell, title, message, initVal)(validator)
   
    def getFilesToRunOn(selection: ISelection, predicate: StringPredicate, monitor: IProgressMonitor): Array[String] = {
      require(selection!=null, "ISelection object cannot be null")
      val progs = SubMonitor.convert(monitor)
      val buffer = new mutable.HashSet[String]
      selection match {
        case structSelect: IStructuredSelection =>
          val itemSize = structSelect.size
          progs.setWorkRemaining(itemSize)
          for( resource <- structSelect.iterator if resource.isInstanceOf[IResource]){
            buffer ++= getFilesUnderResource( resource.asInstanceOf[IResource], predicate, progs.newChild(100/itemSize))
          }
        case _ =>
      }
      buffer.toArray
    }
    
    def getFilesUnderResource(resource: IResource, predicate: StringPredicate, monitor: IProgressMonitor): mutable.Set[String] = {
      require(resource!=null, "IResource object cannot be null.")
      val progs = SubMonitor.convert(monitor)
      val buffer = new mutable.HashSet[String]
      def storeFiles(resource: AnyRef){
    	  progs.setWorkRemaining(1000)
    	  progs.newChild(1)
    	  resource match {
	        case file: IFile if( file.isAccessible && predicate(file.getLocation.toString)) =>
	          buffer += file.getLocation.toString
	        case container: IContainer =>
	          debug("Working to get files under %s", container)
	          container.members foreach(storeFiles)
	        case noMatch: Any =>  debug("%s: is neither a file nor a container. Skipping it", noMatch)
    	  }
      }
      storeFiles(resource)
      if(isDebugEnabled){
        debug("Identified the following files:\n%s", buffer.mkString("\n"))
      }
      buffer
    }
    
    def findOffsets(path: String, lineNos: Array[(Int, Int)], mon: IProgressMonitor) = {
      import org.eclipse.jface.text.{IDocument, Document}
      val document = new Document(path.readAll)
      lineNos.map(lineNo => (document.getLineOffset(lineNo._1), document.getLineOffset(lineNo._2)))
    }
    

    
    def addMarkerToFile(duplicateElement: DuplicateElement, offsets: (Int, Int)): IMarker = {
      def duplicateMessage(duplicateElement: DuplicateElement): String = {
        def toStr(de: DuplicateElement) = de.issueSource.simpleName + "("+ de.startLineNo +" -> " + de.endLineNo + ")"
        val buf = new StringBuilder
        buf.append(duplicateElement.duplicateSet.lineCount)
        	.append(" lines of duplicate @ ").append(toStr(duplicateElement)).append(" with : ")
        
        val dupEls = duplicateElement.duplicateSet.duplicateElements
        val totalDuplicates = dupEls.length
        val toStrDups = if(totalDuplicates > 5) dupEls.slice(0, 5) else dupEls
        buf.append(toStrDups.filter(duplicateElement != _).map(toStr).mkString(" and "))
        if(toStrDups.length < totalDuplicates)
          buf.append(" and #").append(totalDuplicates - toStrDups.length).append(" more...")
        buf.toString
      }
      
      val file = ResourcesPlugin.getWorkspace.getRoot.getFileForLocation(duplicateElement.issueSource.sourcePath)
    if(file!=null){
      import IMarker._
      val marker = file.createMarker(Activator.constant.markerId)
      marker setAttribute(MESSAGE, duplicateMessage(duplicateElement))
      marker setAttribute(USER_EDITABLE, false)
      if(offsets!=null){
	      marker setAttribute(CHAR_START, offsets._1)
	      marker setAttribute(CHAR_END, offsets._2)
      }
      marker setAttribute(LINE_NUMBER, duplicateElement.startLineNo)
      marker setAttribute(PRIORITY, PRIORITY_HIGH)
      marker setAttribute(SEVERITY, SEVERITY_WARNING)
      marker setAttribute("duplicate.warning", true)
      //marker setAttribute("com.googlecode.vanaraha.data.DuplicateElement", duplicateElement)
      return marker
    }
    null
   }
      
   def addMarkersToAllItems(ds: Array[DuplicateSet], mon: IProgressMonitor){
     val progs = SubMonitor.convert(mon)
     val duplicateElements = ds.flatMap(_.duplicateElements)
     progs.worked(10)
     val pathToDupElMap = new mutable.HashMap[String, mutable.Set[DuplicateElement]] with mutable.MultiMap[String, DuplicateElement]
     for(dupEl <- duplicateElements)  
       pathToDupElMap.add(dupEl.issueSource.sourcePath, dupEl)
     progs.worked(10)
     val markerProgs = progs.newChild(80).setWorkRemaining(pathToDupElMap.size)
     for{
    	 keys <- pathToDupElMap; path = keys._1; dupEls = keys._2.toArray
    	 fileProgress = markerProgs.newChild(1)
         offsetWithIndex <- findOffsets(path, 
                                        dupEls.map( dupEl => (dupEl.startLineNo, dupEl.endLineNo)),
     									fileProgress).zipWithIndex
         }{
         fileProgress.setTaskName("Adding marker to file: "+ path)
         addMarkerToFile(dupEls(offsetWithIndex._2), offsetWithIndex._1)
       }
     
   }
   
   def deleteMarkers(selec: ISelection, monitor: IProgressMonitor){
	  val progress = SubMonitor.convert(monitor)
      progress.setTaskName("Clearing duplicates in the selected elements")
      selec match{
        case structSelect: IStructuredSelection =>
            info("Total number of elements to clear markers: %d", structSelect.size)
        	progress.setWorkRemaining(structSelect.size)
        	structSelect.iterator.foreach({ resource =>
        	  	resource match {
        	  	  case res: IResource =>
        	  	    progress.setTaskName("Clearing markers in: " + res.getName)
        	  	    debug("Clearing markers in : " + res+" and all its children")
        	  	    res.deleteMarkers(Activator.constant.markerId, false, IResource.DEPTH_INFINITE)
        	  	  case _ => warn("Object: %s, not an instanceof IResource. Dont know how to handle it!!", resource)
        	  	}
	            progress.worked(1) //whether its a iresource or not, work is done
           })
        case _ => warn("In EclipseUtils#deleteMarkers, received unknown selection:%s", selec)
        //nothing can be done about this.
      }
   }
   
   def job(name: String)(func: IProgressMonitor => IStatus){
     new org.eclipse.core.resources.WorkspaceJob(name){
       override def runInWorkspace(monitor: IProgressMonitor): IStatus = 
         try{
           func(monitor)
         }finally{
           monitor.done
         }
     } schedule
   }
   
   object implicits{
	   import org.eclipse.jface.preference.IPreferenceStore
	   import com.googlecode.vanaraha.utils.PreferenceStore
	   import com.googlecode.vanaraha.tool.ProgressFeedBack
	   implicit def iprefStore2PreferenceStore(store: IPreferenceStore): PreferenceStore = new PreferenceStore{
	     override def apply(str: String) = store.getString(str)
	     override def update(str: String, value: String) = store.setValue(str, value)
	   }
	   
	   implicit def iprogressToProgressFeedBack(progs: IProgressMonitor): ProgressFeedBack = new ProgressFeedBack{
	       override def updateMsg(msg: String) = progs.setTaskName(msg)
	       override def worked(value: Int) = progs.worked(value)
	   }
    
       implicit def strToIPath(str: String): IPath = new Path(str) 
   }
}

